package com.class05.myssm.spring.core;



import com.class05.myssm.spring.anno.Bean;
import com.class05.myssm.spring.anno.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationApplicationContext implements ApplicationContext {
    //    存放bean对象 可以就是一个HashMap
    private ConcurrentHashMap<Class, Object> beanFactory = new ConcurrentHashMap<>();

    private static String rootPath;


    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //创建有参构造，传递包路径，设置包扫描规则,
    // 当前包及其子包，哪个类有@Bean注解，找到注解后把这个类通过反射进行实例化
    public AnnotationApplicationContext(String basePackage) {
        try {
            //1、把.替换成\
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            //2、获取包绝对路径
//                Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(basePackage);
//            TODO
//            com\spring
//            /D:/tmp/pro-web-class05/pro05-reflect/target/classes/com%5cspring
            Enumeration<URL> urls
                    = Thread.currentThread().getContextClassLoader()
                    .getResources(packagePath);

            while (urls.hasMoreElements()) {
//                file:/D:/tmp/pro-web-class05/pro05-reflect/target/classes/com%5cspring
                URL url = urls.nextElement();
//               /D:/tmp/pro-web-class05/pro05-reflect/target/classes/com\spring
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //3、获取包前面路径部分，字符按串截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                //4、包扫描
                loadBean(new File(filePath));
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

//        依赖注入
        loadDi();

    }

    //包扫描，实例化
//    D:\tmp\pro-web-class05\pro05-reflect\target\classes\com\spring
//    D:\tmp\pro-web-class05\pro05-reflect\target\classes\com\spring\anno
    private void loadBean(File file) {
        try {
//       1、判断当前是否是文件夹
            if (file.isDirectory()) {
                //       2、获取文件夹里面所有内容

                File[] childrenFiles = file.listFiles();
                //       3、判断文件夹里面为空，直接返回
                if (childrenFiles == null || childrenFiles.length == 0) {
                    return;
                }
                //       4、 如果文件夹里面不为空，遍历文件夹里面所有内容
                for (File child : childrenFiles) {
                    //       4.1、遍历得到每个File对象，继续判断，如果还是文件夹，递归
                    if (child.isDirectory()) {
                        //递归
                        loadBean(child);
                    } else {
                        //       4.2、遍历得到File对象不是文件夹，是文件
                        //       4.3、得到包路径+类名称部分-字符串截取
                        //TODO  (正确com\spring\anno\Bean.class)
//                        om\spring\anno\Bean.class
//
                        String pathwithClass = child.getAbsolutePath()
                                .substring(rootPath.length()-1 );
                        //       4.4、判断当前文件类是否为.class
                        if (pathwithClass.contains(".class")) {
                            //       4.5、如果是.class 类型，把路径\替换成.  把.class去掉
                            String allName = pathwithClass.replaceAll("\\\\", ".")
                                    .replace(".class", "");

                            //       4.6、判断类上面是否有注解@Bean,如果有进行实例化过程
                            //       4.6.1、获取类的Class
                            Class<?> clazz = Class.forName(allName);
                            //       4.6.2、判断不是接口
                            if (!clazz.isInterface()) {
                                //       4.6.3、判断类上面是否有注解  @Bean
                                Bean annotation = clazz.getAnnotation(Bean.class);
                                if (annotation != null) {
                                    Object instance = clazz.getConstructor().newInstance();
                                    //       4.7、把对象实例化之后，放到ConcurrentHashMap集合beanFactory
                                    //       4.7.1判断当前类如果有接口，让接口class作为map的key
                                    if (clazz.getInterfaces().length > 0) {
                                        beanFactory.put(clazz.getInterfaces()[0], instance);
                                    } else {
                                        beanFactory.put(clazz, instance);
                                    }
                                }

                            }
                        }

                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }


    //    属性注入
    private void loadDi() {
        //        实例化对象在beanFactory的ConcurrentHashMap集合里
//        1、遍历beanFactory的ConcurrentHashMap集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();

        for (Map.Entry<Class, Object> entry : entries) {
            //        2.1、获取对象Class
            Object obj = entry.getValue();
            Class<?> clazz = obj.getClass();
            //        2.2、获取每个对象属性取到
            Field[] declaredFields = clazz.getDeclaredFields();
            //        3、遍历得到每个对象属性数组，得到每个属性
            for (Field field : declaredFields) {
                Di annotation = field.getAnnotation(Di.class);
                //        4、判断属性上面是否有@Di注解
                if (annotation != null) {
                    //        4.1、如果私有属性，设置可以设置值
                    field.setAccessible(true);
                }
                try {
                    //        5、如果有@Di注解，把对象进行设置（注入）
                    field.set(obj, beanFactory.get(field.getType()));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }

            }
        }


    }
}
